﻿using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System.Windows.Forms;
using System.Diagnostics;
using SwParaPlatform2.logger;
using SwParaPlatform2.tools;  // For folder selection

namespace SwParaPlatform2
{
    public class SolidWorksPathUpdater
    {
        private ISldWorks swApp;
        private Dictionary<string, string> CopyPathDict;
        private List<string> ReachedPath = new List<string>();

        private static readonly object _lock = new object();
        private static SolidWorksPathUpdater instance;
        private static bool _isInitialized = false;

        public SolidWorksPathUpdater(ISldWorks swApp)
        {
            this.swApp = swApp;
            CopyPathDict = new Dictionary<string, string>();
            ReachedPath = new List<string>();
        }

        public static SolidWorksPathUpdater Initialize(ISldWorks swApp)
        {
            if (instance == null)
            {
                lock (_lock)
                {
                    if (instance == null)
                    {
                        instance = new SolidWorksPathUpdater(swApp);
                        _isInitialized = true;
                    }
                }
            }
            return instance;
        }

        // 获取实例，不需要再次传入参数
        public static SolidWorksPathUpdater Instance
        {
            get
            {
                if (!_isInitialized)
                    Logger.Instance.Log("SolidWorksPathUpdater，请先调用Initialize方法！");
                return instance;
            }
        }

        public static void Reset()
        {
            instance = null;
        }

        // 打开文件管理器选择文件夹
        public string GetTargetDirectoryFromFileExplorer()
        {
            using (var folderDialog = new FolderBrowserDialog())
            {
                folderDialog.Description = "请选择目标文件夹";
                if (folderDialog.ShowDialog() == DialogResult.OK)
                {
                    return folderDialog.SelectedPath;
                }
                else
                {
                    throw new Exception("未选择目标文件夹");
                }
            }
        }
        // 复制所有引用的文件并修改引用路径
        public string CopyAndModifyReferences(string targetDirectory, string mainFilePath, string CopyType)
        {
            IModelDoc2 activeDoc = (IModelDoc2)swApp.ActiveDoc;
            IModelDoc2 copyDoc = activeDoc;

            if (File.Exists(mainFilePath))
            {
                Logger.Instance.Log("文件存在！");
            }
            else
            {
                Logger.Instance.Log("文件不存在！");
                return mainFilePath;
            }

            if (activeDoc.GetPathName() != mainFilePath)
            {
                int docType = 0;

                if (mainFilePath.EndsWith(".sldprt", StringComparison.OrdinalIgnoreCase))
                {
                    docType = (int)swDocumentTypes_e.swDocPART;
                }
                else if (mainFilePath.EndsWith(".sldasm", StringComparison.OrdinalIgnoreCase))
                {
                    docType = (int)swDocumentTypes_e.swDocASSEMBLY;
                }

                if (swApp != null && docType != 0)
                {
                    Logger.Instance.Log($"Open Doc Success:{mainFilePath}");
                    int errors = 0;
                    int warnings = 0;
                    SolidWorksHelper helper = new SolidWorksHelper();
                    copyDoc = helper.IsDocumentOpen(mainFilePath);
                    if(copyDoc == null)
                        copyDoc = swApp.OpenDoc6(mainFilePath, docType, (int)swOpenDocOptions_e.swOpenDocOptions_Silent, "", ref errors, ref warnings);
                    
                    if (errors != 0 || warnings != 0)
                    {
                        Logger.Instance.Log($"插入基础零件失败，请联系技术人员解决：Error = {errors}, Warning = {warnings}");
                    }
                }
            }
            
            if (copyDoc == null)
            {
                throw new Exception("没有活动文档");
            }

            // 创建目标路径中的 model 文件夹
            string modelDirectory = Path.Combine(targetDirectory, CopyType);
            Directory.CreateDirectory(modelDirectory);

            // 获取主文件路径
            string mainFileName = Path.GetFileNameWithoutExtension(mainFilePath);
            string mainFileTempPath = Path.Combine(modelDirectory, $"{mainFileName}_temp{Path.GetExtension(mainFilePath)}");

            // 将所有子组件复制到主文件同级目录
            ProcessComponentsRecursively(copyDoc, modelDirectory, mainFilePath);

            // 复制主文件并重命名为_temp
            File.Copy(mainFilePath, mainFileTempPath, true);
            CopyPathDict[mainFilePath] = mainFileTempPath;

            // 更新主文件的引用路径为相对路径
            UpdateReferencesToRelativePaths(mainFileTempPath, modelDirectory, copyDoc);

            // 将_temp文件后缀去掉
            string mainFileFinalPath = Path.Combine(modelDirectory, $"{mainFileName}{Path.GetExtension(mainFilePath)}");

            if (File.Exists(mainFileFinalPath))
            {
                File.Delete(mainFileFinalPath);
            }

            File.Move(mainFileTempPath, mainFileFinalPath);
            DrawingManager.GetInstance(swApp).CopyAndAssociateDrawing(mainFilePath, mainFileFinalPath);

            CopyPathDict = new Dictionary<string, string>();

            if (activeDoc.GetPathName() != mainFilePath)
                swApp.CloseDoc(copyDoc.GetPathName());
                // 返回相对路径
            return GetRelativePath(mainFileFinalPath, targetDirectory);
        }

        // 递归处理装配体中的所有子组件
        private void ProcessComponentsRecursively(IModelDoc2 modelDoc, string targetDirectory, string mainFilePath)
        {
            IAssemblyDoc assemblyDoc = modelDoc as IAssemblyDoc;
            if (assemblyDoc == null) return;

            object[] components = (object[])assemblyDoc.GetComponents(false);
            if(components != null)
            {
                foreach (IComponent2 component in components)
                {
                    if (ReachedPath.Contains(component.GetPathName()))
                        continue;

                    bool wasSuppressed = component.IsSuppressed();
                    // 如果组件被压缩（抑制）
                    if (wasSuppressed)
                        continue;

                    string componentPath = component.GetPathName();
                    Logger.Instance.Log($"处理 componentPath = {componentPath}");
                    if (!string.IsNullOrEmpty(componentPath) && !componentPath.Equals(mainFilePath, StringComparison.OrdinalIgnoreCase))
                    {
                        string newComponentPath = Path.Combine(targetDirectory, Path.GetFileName(componentPath));
                        CopyPathDict[componentPath] = newComponentPath;

                        // 复制子组件到主文件同级目录
                        File.Copy(componentPath, newComponentPath, true);
                        DrawingManager.GetInstance(swApp).CopyAndAssociateDrawing(componentPath, newComponentPath);
                        // 如果子组件是装配体，递归处理它的子组件
                        IModelDoc2 subComponentDoc = (IModelDoc2)component.GetModelDoc2();
                        if (subComponentDoc != null && subComponentDoc is IAssemblyDoc)
                        {
                            ProcessComponentsRecursively(subComponentDoc, targetDirectory, mainFilePath);
                        }
                    }
                    ReachedPath.Add(componentPath);
                }
            }
            
        }

        // 获取相对路径
        private string GetRelativePath(string absolutePath, string relativeTo)
        {
            Uri pathUri = new Uri(absolutePath);
            if (!relativeTo.EndsWith(Path.DirectorySeparatorChar.ToString()))
            {
                relativeTo += Path.DirectorySeparatorChar;
            }
            Uri folderUri = new Uri(relativeTo);
            return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
        }

        private string GetFileRelativePath(string absolutePath, string relativeTo)
        {
            Uri absoluteUri = new Uri(absolutePath);
            Uri relativeUri = new Uri(relativeTo);

            // 获取相对于relativeTo的目录的Uri
            Uri relativeUriDirectory = new Uri(Path.GetDirectoryName(relativeTo) + Path.DirectorySeparatorChar);

            // 计算相对Uri
            Uri relativePathUri = relativeUriDirectory.MakeRelativeUri(absoluteUri);

            // 将Uri格式转为普通路径格式
            string relativePath = Uri.UnescapeDataString(relativePathUri.ToString());

            // 替换Uri中的'/'为系统的目录分隔符
            relativePath = relativePath.Replace('/', Path.DirectorySeparatorChar);

            return relativePath;
        }


        // 更新模型引用路径为相对路径
        public void UpdateReferencesToRelativePaths(string modelPath, string targetDirectory, IModelDoc2 copyDoc)
        {
            int errors = 0;
            int warnings = 0;

            int iDocType = copyDoc.GetType();

            if (!File.Exists(modelPath))
            {
                Logger.Instance.Log($"File does not exist: {modelPath}");
                return;
            }

            // 打开复制后的模型文档
            IModelDoc2 modelDoc = swApp.OpenDoc6(modelPath, iDocType,
                (int)swOpenDocOptions_e.swOpenDocOptions_Silent, "", ref errors, ref warnings);

            //Open client model window containing the active document
            //swApp.CreateNewWindow();

            if (modelDoc == null)
            {
                Logger.Instance.Log($"无法打开复制出来的模型文档{errors}, {warnings}");
                throw new Exception("无法打开复制出来的模型文档");
            }

            if(copyDoc.GetType() == (int)swDocumentTypes_e.swDocASSEMBLY)
            {
                IAssemblyDoc assemblyDoc = modelDoc as IAssemblyDoc;

                // 递归处理装配体中的子组件，更新引用路径
                ProcessComponentsForRelativePathUpdate(assemblyDoc, targetDirectory);
            }
            

            // 保存模型并关闭文档
            modelDoc.Save();
            swApp.CloseDoc(modelPath);
        }

        // 递归处理装配体中的所有子组件，更新它们的路径为相对路径
        private void ProcessComponentsForRelativePathUpdate(IAssemblyDoc assemblyDoc, string targetDirectory)
        {
            object[] components = (object[])assemblyDoc.GetComponents(false);
            if(components != null)
            {
                foreach (IComponent2 component in components)
                {
                    bool wasSuppressed = component.IsSuppressed();
                    // 如果组件被压缩（抑制）
                    if (wasSuppressed)
                        continue;

                    string componentPath = component.GetPathName();
                    if (!string.IsNullOrEmpty(componentPath))
                    {
                        //string relativePath = GetFileRelativePath(componentPath, targetDirectory);
                        //string newComponentPath = Path.Combine(targetDirectory, relativePath);
                        Logger.Instance.Log($"componentPath = {componentPath}");

                        bool HasValue = CopyPathDict.TryGetValue(componentPath, out string newComponentPath);
                        if (HasValue)
                        {
                            
                            UpdateComponentPath(assemblyDoc, component, newComponentPath);
                        }
                        else
                        {
                            Logger.Instance.Log($"CopyPathDict 不包含 {componentPath}");
                        }

                        // 如果该组件是装配体，递归处理它的子组件
                        IModelDoc2 componentDoc = (IModelDoc2)component.GetModelDoc2();
                        if (componentDoc != null && componentDoc is IAssemblyDoc subAssemblyDoc)
                        {
                            ProcessComponentsForRelativePathUpdate(subAssemblyDoc, targetDirectory);
                        }
                    }
                }
            }
            
        }

        // 更新组件路径
        private void UpdateComponentPath(IAssemblyDoc assemblyDoc, IComponent2 component, string newPath)
        {
            IModelDoc2 modelDoc = (IModelDoc2)assemblyDoc;
            ISelectionMgr selectionMgr = modelDoc.SelectionManager;
            SelectData SwSelData = selectionMgr.CreateSelectData();
            modelDoc.ClearSelection2(true);
            bool status = component.Select4(false, SwSelData, false);
            string ReferencedConfiguration = component.ReferencedConfiguration;
            bool success = assemblyDoc.ReplaceComponents2(newPath, ReferencedConfiguration, true, 0, true);
            Logger.Instance.Log($"Replace {component.Name2} to {newPath} = {success}, select = {status}");
        }

        public string CreateHiddenConfigDirectory(IModelDoc2 copyDoc)
        {
            if (copyDoc == null)
            {
                throw new Exception("没有活动文档");
            }

            // 获取活动文档的完整路径
            string fullPath = copyDoc.GetPathName();

            if (string.IsNullOrEmpty(fullPath))
            {
                throw new Exception("活动文档尚未保存");
            }

            // 获取文件所在目录路径
            string directoryPath = Path.GetDirectoryName(fullPath);

            DataStore.Instance.sParentPath = directoryPath;

            // 获取文件名（不含路径）
            string fileName = Path.GetFileNameWithoutExtension(fullPath);

            // 拼接新的隐藏配置文件夹名
            string configFolderName = "_" + fileName + "_Config";
            string configFolderPath = Path.Combine(directoryPath, configFolderName);

            // 拼接.yxdcfg文件路径
            string configFileName = fileName + "_Config.yxdcfg";
            string configFilePath = Path.Combine(directoryPath, configFileName);

            // 拼接临时.yxdcfg文件路径
            string configFileName_Temp = "_" + fileName + "_Config.yxdcfg";
            string configFilePath_Temp = Path.Combine(directoryPath, configFileName_Temp);

            // 检查文件夹或.yxdcfg文件是否存在
            if (Directory.Exists(configFolderPath) || File.Exists(configFilePath))
            {
                // 提示用户是否继续并覆盖
                DialogResult result = MessageBox.Show(
                    $"文件夹 {configFolderName} 或文件 {configFileName} 已存在，是否继续创建并覆盖？",
                    "文件夹/文件已存在",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning
                );

                if (result == DialogResult.No)
                {
                    // 用户选择了不覆盖，中止流程
                    throw new Exception("操作被用户取消，未创建新文件夹。");
                }

                // 用户选择了覆盖，删除已有文件夹及文件
                if (Directory.Exists(configFolderPath))
                {
                    object[] docs = (object[])swApp.GetDocuments();
                    Logger.Instance.Log($"当前打开的文档数量：{docs.Length}");
                    if (docs != null)
                    {
                        foreach (ModelDoc2 doc in docs)
                        {
                            string name = doc.GetTitle();
                            swApp.CloseDoc(name);
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
                        }

                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                    }
                    Logger.Instance.Log($"删掉同名文件夹 in LoadAllDictionaryFromXml");
                    DirectoryHelper.ForceDeleteDirectory(configFolderPath);
                    //Directory.Delete(configFolderPath, true);  // 递归删除文件夹
                }

                if (File.Exists(configFilePath))
                {
                    File.Delete(configFilePath);
                }

                if (File.Exists(configFilePath_Temp))
                {
                    File.Delete(configFilePath_Temp);
                }
            }

            // 创建文件夹
            Directory.CreateDirectory(configFolderPath);

            // 设置文件夹为隐藏
            SetFolderHiddenAttribute(configFolderPath);

            // 返回配置文件夹的路径
            return configFolderPath;
        }

        private void SetFolderHiddenAttribute(string folderPath)
        {
            DirectoryInfo dirInfo = new DirectoryInfo(folderPath);

            // 设置文件夹的隐藏属性
            dirInfo.Attributes |= FileAttributes.Hidden;
        }
    }

    public class SolidWorksHelper
    {
        private SldWorks swApp;

        public SolidWorksHelper()
        {
            // 获取当前SolidWorks应用程序对象
            swApp = (SldWorks)Activator.CreateInstance(Type.GetTypeFromProgID("SldWorks.Application"));
            if (swApp == null)
            {
                throw new Exception("无法连接到SolidWorks实例，请确保SolidWorks已启动。");
            }
        }

        /// <summary>
        /// 检查指定文件名的SolidWorks文档是否已打开
        /// </summary>
        /// <param name="filePath">要检查的文档完整路径</param>
        /// <returns>如果已打开，返回打开的IModelDoc2对象；否则返回null</returns>
        public IModelDoc2 IsDocumentOpen(string filePath)
        {
            try
            {
                string targetFileName = Path.GetFileName(filePath);
                if (string.IsNullOrEmpty(targetFileName))
                {
                    Logger.Instance.Log("文件名不能为空！");
                    return null;
                }

                int docCount = swApp.GetDocumentCount();
                if (docCount == 0)
                {
                    Logger.Instance.Log("没有打开的文档。");
                    return null;
                }

                ModelDoc2 doc = (ModelDoc2)swApp.GetFirstDocument();
                while (doc != null)
                {
                    string openedFileName = Path.GetFileName(doc.GetPathName());

                    // 仅比较文件名（忽略大小写）
                    if (string.Equals(openedFileName, targetFileName, StringComparison.OrdinalIgnoreCase))
                    {
                        Logger.Instance.Log($"文档已打开: {openedFileName}");
                        return doc;
                    }

                    // 获取下一个打开的文档
                    doc = (ModelDoc2)doc.GetNext();
                }

                Logger.Instance.Log("文档未打开。");
                return null;
            }
            catch (Exception ex)
            {
                Logger.Instance.Log($"检查文档打开状态时出错: {ex.Message}");
                return null;
            }
        }
    }
}
